#include <iostream>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>
using namespace std;
#define GETKEYERROR -1
#define MSGCREATEERROR -2
#define FORKERROR -3

// 自定义消息结构体，避免与系统 struct msgbuf 冲突，并为文本留足空间
struct MyMsg {
    long mtype;
    char mtext[128];
};

int main()
{
    key_t key = ftok(".", 65); //生成唯一键值
    if(key == -1) {
        perror("ftok");
        return GETKEYERROR;
    }
    // 使用 IPC_CREAT，如果队列已存在则直接打开，避免因 IPC_EXCL 失败
    int msgid = msgget(key, 0666 | IPC_CREAT ); //创建/打开消息队列
    if(msgid == -1) {
        perror("msgget");
        return MSGCREATEERROR;
    }
    pid_t pid = fork(); //创建子进程
    if(pid == -1) {
        perror("fork");
        return FORKERROR;
    }
    if(pid == 0)
    {
        //子进程
        //从消息队列读取数据
        MyMsg message;
        while(true)
        {
            if(msgrcv(msgid, &message, sizeof(message.mtext), 1, 0) == -1) {
                perror("msgrcv");
                break;
            }
            cout << "Received message: " << message.mtext << endl;
        }
        exit(0);
    }
    //父进程
    //写数据到消息队列
    MyMsg message;
    int cnt = 5;
    while(cnt--)
    {
        message.mtype = 1; //消息类型
        snprintf(message.mtext, sizeof(message.mtext), "Hello child! I am a father process cnt:%d", 5 - cnt);
        if(msgsnd(msgid, &message, sizeof(message.mtext), 0) == -1) {
            perror("msgsnd");
            break;
        }
        sleep(1);
    }
    // 先删除消息队列，解除子进程阻塞（msgrcv 将返回 -1 且 errno=EIDRM）
    if(msgctl(msgid, IPC_RMID, nullptr) == -1) {
        perror("msgctl(IPC_RMID)");
    }
    int ret = waitpid(pid, nullptr, 0);
    if(ret == pid)
        cout << "child is reaped successfully." << endl;
    return 0;
}